Passed
Branch master (32b4c5)
by Askupa
01:33
created

Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.doScroll   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 21

Duplication

Lines 21
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
c 1
b 0
f 1
nc 9
nop 2
dl 21
loc 21
rs 8.7624
1
Mivhak.component('horizontal-scrollbar', {
2
    template: '<div class="mivhak-scrollbar mivhak-h-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>',
3
    props: {
4
        editor: null,
5
        $inner: null,
6
        $outer: null,
7
        mivhakInstance: null,
8
        minWidth: 50,
9
        state: {
10
            a: 0,    // The total width of the editor
11
            b: 0,    // The width of the viewport, excluding padding
12
            c: 0,    // The width of the viewport, including padding
13
            d: 0,    // The calculated width of the thumb
14
            l: 0     // The current left offset of the viewport
15
        },
16
        initialized: false
17
    },
18
    methods: {
19
        initialize: function() {
20
            if(!this.initialized)
21
            {
22
                this.initialized = true;
23
                this.dragDealer();
24
                var $this = this;
25
                $(window).resize(function(){
26
                    if(!$this.mivhakInstance.state.lineWrap)
27
                        $this.refresh();
28
                });
29
            }
30
            this.refresh();
31
        },
32
        updateState: function() {
33
            var oldState = $.extend({}, this.state);
34
            this.state.a = this.getEditorWidth();
35
            this.state.b = this.$outer.parent().width();
36
            this.state.c = this.state.b - this.mivhakInstance.options.padding*2;
37
            this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minWidth);
38
            this.state.l *=  this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero
39
            return this.state.a !== oldState.a || this.state.b !== oldState.b;
40
        },
41
        refresh: function() {
42
            var $this = this, oldLeft = this.state.l;
43
            raf(function(){
44
                if($this.updateState())
45
                {
46
                    if($this.getDifference() > 0)
47
                    {
48
                        $this.doScroll('left',oldLeft-$this.state.l);
49
                        $this.$el.css({width: $this.state.d + 'px', left: 0});
50
                        $this.moveBar();
51
                    }
52
                    else 
53
                    {
54
                        $this.doScroll('left',$this.state.l);
55
                        $this.$el.css({width: 0});
56
                    }
57
                }
58
            });
59
        },
60
        dragDealer: function(){
61
            var $this = this,
62
                lastPageX;
63
64
            this.$el.on('mousedown.drag', function(e) {
65
                lastPageX = e.pageX;
66
                $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed');
67
                $(document).on('mousemove.drag', drag).on('mouseup.drag', stop);
68
                return false;
69
            });
70
71
            function drag(e){
72
                var delta = e.pageX - lastPageX,
73
                    didScroll;
74
75
                // Bail if the mouse hasn't moved
76
                if(!delta) return;
77
            
78
                lastPageX = e.pageX;
79
                
80
                raf(function(){
81
                    didScroll = $this.doScroll(delta > 0 ? 'right' : 'left', Math.abs(delta*$this.getEditorWidth()/$this.$outer.parent().width()));
82
                    if(0 !== didScroll) $this.moveBar();
83
                });
84
            }
85
86
            function stop() {
87
                $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed');
88
                $(document).off("mousemove.drag mouseup.drag");
89
            }
90
        },
91
        moveBar: function() {
92
            this.$el.css({
93
                left:  (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.l + 'px'
94
            });
95
        },
96
        
97
        /**
98
         * Scrolls the editor element in the direction given, provided that there 
99
         * is remaining scroll space
100
         * @param {string} dir
101
         * @param {int} delta
102
         */
103
        doScroll: function(dir, delta) {
104
            var s = this.state,
105
                remaining,
106
                didScroll;
107
            
108
            if('left' === dir) 
109
            {
110
                remaining = s.l;
111
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
112
                s.l -= didScroll;
113
            }
114
            if('right' === dir) 
115
            {
116
                remaining = this.getDifference() - s.l;
117
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
118
                s.l += didScroll;
119
            }
120
            
121
            this.$inner.find('.ace_content').css({'margin-left': -s.l});
122
            return didScroll;
123
        },
124
        
125
        /**
126
         * Returns the difference between the containing div and the editor div
127
         */
128
        getDifference: function()
129
        {
130
            return this.state.a - this.state.c;
131
        },
132
        
133
        /**
134
         * Calculate the editor's width based on the number of lines
135
         */
136
        getEditorWidth: function() {
137
            return this.$inner.find('.ace_content').width();
138
        }
139
    }
140
});